﻿Imports System.Reflection
Imports System.Collections.Specialized
Imports System.Collections.ObjectModel
Imports Kairyu.Load
Imports System.Threading.Tasks

<Obsolete("CollectionInitializerを使用してください")>
Public Class CollectionFactory
    ''' <summary>
    ''' コレクションプロパティを初期化します。既存アイテムは破棄されます
    ''' </summary>
    Public Shared Function Init(Of T)(instance As T) As T
        Dim q = (From el In instance.GetType.ToTable.ChildRelations)
        Return Init(instance, q)
    End Function

    ''' <summary>
    ''' 該当プロパティのインスタンスを生成します。既存アイテムは破棄されます
    ''' </summary>
    Public Shared Function Init(Of T)(instance As T, childProp As PropertyInfo) As T
        Dim q = (From el In instance.GetType.ToTable.ChildRelations Where el.CollectionProperty.Equals(childProp))
        Return Init(instance, q)
    End Function

    ''' <summary>
    ''' 該当プロパティのインスタンスを生成します。既存アイテムは破棄されます
    ''' </summary>
    Public Shared Function Init(Of T, ChildT)(instance As T) As T
        Dim q = (From el In instance.GetType.ToTable.ChildRelations Where el.CollectionProperty.PropertyType.GetGenericArguments.First.Equals(GetType(ChildT)))
        Return Init(instance, q)
    End Function

    ''' <summary>
    ''' 再定義します
    ''' 既存アイテムは維持されます
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks>
    ''' 親子関係の再構築時に使用予定。
    ''' おそらくWebFormを使用した場合、ObservableCollectionの親子結合ロジックは無視されると思われる。
    ''' </remarks>
    Friend Shared Function ReDifine(Of T)(instance As T) As T
        Dim q = (From el In instance.GetType.ToTable.ChildRelations)
        Return ReDifine(instance, q)
    End Function

    ''' <summary>
    ''' コレクションプロパティをLazyListで初期化します
    ''' </summary>
    Friend Shared Function InitAsLasy(Of T, ChildT)(instance As T, loader As ModelLoader, loadQuery As Query(Of ChildT)) As T
        Dim q = (From el In instance.GetType.ToTable.ChildRelations Where el.CollectionProperty.PropertyType.GetGenericArguments.First.Equals(GetType(ChildT)))
        Return InitAsLazy(instance, q, loader, loadQuery)
    End Function

    Private Shared Function ReDifine(Of T)(instance As T, q As IEnumerable(Of ChildRelation)) As T
        For Each item In q
            Dim oldList = item.CollectionProperty.GetValue(instance, Nothing)
            Dim newList = CreateCollection(instance, item)

            item.CollectionProperty.SetValue(instance, newList, Nothing)

            '登録済みアイテムを新しいリストに移す
            If oldList IsNot Nothing Then
                For Each oldITem In oldList
                    newList.Add(oldITem)
                Next
            End If
        Next
        Return instance
    End Function

    Private Shared Function Init(Of T)(instance As T, q As IEnumerable(Of ChildRelation)) As T
        For Each item In q
            Dim newList = CreateCollection(instance, item)
            item.CollectionProperty.SetValue(instance, newList, Nothing)
        Next
        Return instance
    End Function

    Private Shared Function InitAsLazy(Of T, ChildT)(instance As T, q As IEnumerable(Of ChildRelation), loader As ModelLoader, loadQuery As Query(Of ChildT)) As T
        Throw New NotSupportedException("CollectionInitializerを使用してください")
        'For Each item In q
        '    Dim newList = New LazyList(Of ChildT)(CreateCollection(instance, item), loader, loadQuery)
        '    item.CollectionProperty.SetValue(instance, newList, Nothing)
        'Next
        'Return instance
    End Function

    Private Shared Function CreateCollection(instance As Object, rel As ChildRelation) As IList
        Dim childProp = rel.CollectionProperty
        Dim childType = childProp.PropertyType.GetGenericArguments.First
        Dim parentProp = rel.YourColumn.PropertyInfo

        Return CreateCollection(instance, childProp, childType, parentProp)
    End Function

    Private Shared Function CreateCollection(instance As Object, childProp As PropertyInfo, childType As Type, parentProp As PropertyInfo) As IList
        '方法 : リフレクションを使用してジェネリック型をチェックおよびインスタンス化する
        'http://msdn.microsoft.com/ja-jp/library/b8ytshk6(v=vs.100).aspx
        Dim lstType As Type = GetType(ObservableCollection(Of )).MakeGenericType({childType})
        Dim lst = Activator.CreateInstance(lstType)

        '親との関連を強制化する
        '方法 : リフレクションを使用してデリゲートをフックする
        'http://msdn.microsoft.com/ja-jp/library/ms228976(v=vs.100).aspx

        'イベントを表す EventInfo オブジェクトを取得し、EventHandlerType プロパティを使用して、イベントの処理に使用するデリゲートの型を取得します。
        Dim eventCollectionChanged As EventInfo = lst.GetType.GetEvent("CollectionChanged")
        Dim delegateType As Type = eventCollectionChanged.EventHandlerType

        'イベントを処理するメソッドを表す MethodInfo オブジェクトを取得します
        Dim target As Object = Nothing

        If PropertyReader.IsExtensionCollection(childProp) Then
            target = New ParentAdderForExtension With {.Parent = instance, .ParentProp = parentProp}
        Else
            target = New ParentAdder With {.Parent = instance, .ParentProp = parentProp}
        End If
        Dim miHandler = target.GetType.GetMethod("OnCollectionChanged")

        'CreateDelegate メソッドを使用して、デリゲートのインスタンスを作成します。
        Dim d As [Delegate] = [Delegate].CreateDelegate(delegateType, target, miHandler)

        'add アクセサー メソッドを取得し、このメソッドを呼び出してイベントをフックします。
        Dim miAddHandler As MethodInfo = eventCollectionChanged.GetAddMethod()
        Dim addHandlerArgs() As Object = {d}
        miAddHandler.Invoke(lst, addHandlerArgs)

        Return lst
    End Function

    ''' <summary>
    ''' Childコレクション用イベント処理
    ''' </summary>
    ''' <remarks></remarks>
    Private Class ParentAdder
        Property ParentProp As PropertyInfo
        Property Parent As Object
        Sub OnCollectionChanged(sender As Object, e As NotifyCollectionChangedEventArgs)
            If e.Action <> NotifyCollectionChangedAction.Add Then Return
            If ParentProp Is Nothing Then Return
            For Each item In e.NewItems
                ParentProp.SetValue(item, Parent, Nothing)
            Next
        End Sub
    End Class

    ''' <summary>
    ''' Extensionコレクション用イベント処理
    ''' </summary>
    ''' <remarks></remarks>
    Private Class ParentAdderForExtension
        Property ParentProp As PropertyInfo
        Property Parent As Object
        Sub OnCollectionChanged(sender As Object, e As NotifyCollectionChangedEventArgs)
            If e.Action <> NotifyCollectionChangedAction.Add Then Return
            If ParentProp Is Nothing Then Return

            For Each item In e.NewItems
                If sender.Count > 1 Then
                    sender.Clear()
                    Throw New ManyExtensionException
                Else
                    ParentProp.SetValue(item, Parent, Nothing)
                End If
            Next
        End Sub
    End Class

End Class